package com.AdvancedDemos.静态成员变量_方法_代码块;

import java.util.Random;

public class 静态成员变量_方法_代码块 {
    public static void main(String[] args) {

        //在程序执行前,.class字节码文件只会加载到方法区中一次,此后每次将某个类实例化时无需加载,模版只需要一个

        //static修饰成员变量,静态化成员变量(又叫类变量),需要public修饰,只在程序执行前将.class字节码文件加载到方法区中时(),在方法区中开辟空间实例化一次,静态成员变量加载在方法区中,不在堆中,之后每次创建对象时,不再赋值,所有对象共享该静态成员变量

        // 静态成员变量直接使用: 类名.静态成员变量名 即可 , 在类内部使用本类静态成员变量不需要加类名,直接使用即可 , 使用 对象.静态成员变量名 老版本也可以,但不推荐

        Student stu1 = new Student("王", 1);
        Student stu2 = new Student("张", 2);
        System.out.print(stu1.getName() + " ");
        System.out.println(stu1.getId());
        System.out.print(stu2.getName() + " ");
        System.out.println(stu2.getId());

//        System.out.println(stu1.test);//报错:'test' 在 'com.就业班.静态成员变量_方法_代码块.Student' 中具有 private 访问权限 因为静态成员变量test声明成private,会导致类外不可见,应该声明为public

        Student.test = "test";
        System.out.println(Student.test);//test

        stu1.show();
        Student.test = "helloworld";
        stu2.show();//helloworld
        Student stu3 = new Student("李", 3);
        stu3.show();//尽管stu3在Student.test = "helloworld"之后定义的,但是静态成员成员变量是共享的,所以结果是helloworld

        /*王 1
        张 2
        test
        王 - 1 - test
        张 - 2 - helloworld
        李 - 3 - helloworld*/


        //static修饰的成员方法是静态成员方法,一般需要public修饰,private修饰将导致类外不可见
        //使用 类名.静态成员方法名() 调用
        //一般用于util包中的工具类
        Student.haha();

        for (int i = 0; i < 5; i++) {
            System.out.println(MyUtility.getCheckCode());
        }
        //注意事项:
        //1.类方法可以直接访问类成员,不可以直接访问实例成员,也不可以调用实例方法,因为类方法不属于某一个实例
        //2.实例方法中既可以直接访问类成员,也可以直接访问实例成员
        //3.实例方法中可以出现this关键字,类方法中不可以出现this关键字


        //测试3种代码块
        System.out.println("------------------");
        CodeBlock cb1 = new CodeBlock();
        CodeBlock cb2 = new CodeBlock();
        CodeBlock cb3 = new CodeBlock();
        System.out.println("------------------");
        /*
            ------------------
            1 静态代码块执行了
            2 构造代码块执行了
            3 空参构造中的局部代码块被调用了
            2 构造代码块执行了
            3 空参构造中的局部代码块被调用了
            2 构造代码块执行了
            3 空参构造中的局部代码块被调用了
            ------------------
        * */

        //静态代码块会与静态成员变量一起在.class字节码文件向方法区加载时执行一次,此后不再执行;静态代码块可以用于给 静态成员变量 赋值

        //构造代码块优先于构造函数中的内容执行,每次new新对象两者都会先后执行

        //局部代码块用在函数内部,用于提前结束局部变量的生命周期

        System.out.println("a = " + CodeBlock.a);//最终a的值是静态代码块中的2,而不是1
        System.out.println("b = " + CodeBlock.b);
        System.out.println("c = " + CodeBlock.c);

        //a = 2
        //b = 1
        //c = 5


        System.out.println("------------------");
        System.out.println(SingleInstance.getSingleInstanceObject());
        System.out.println(SingleInstance.getSingleInstanceObject());
        System.out.println(SingleInstance.getSingleInstanceObject());
        /*
        com.就业班.静态成员变量_方法_代码块.SingleInstance@254989ff
        com.就业班.静态成员变量_方法_代码块.SingleInstance@254989ff
        com.就业班.静态成员变量_方法_代码块.SingleInstance@254989ff

        可以看到3次调用返回的是同一个实例,地址相同
        * */
        System.out.println("------------------");
        System.out.println(SingleInstance2.getSingleInstance2Object());
        System.out.println(SingleInstance2.getSingleInstance2Object());

        /*
        com.就业班.静态成员变量_方法_代码块.SingleInstance2@37f8bb67
        com.就业班.静态成员变量_方法_代码块.SingleInstance2@37f8bb67

        第一次调用创建唯一实例,第二次调用不再开空间创建实例

        */
        System.out.println("------------------");

    }
}

/**
 * "单例设计模式(单一实例)( 之 饿汉模式,无论将来是否使用到工具类中的唯一实例,都先创建,以空间换时间)
 * ,类外无法创建实例,只在类内创建唯一实例,外部可以通过类内提供的函数获得唯一实例,可以使用唯一实例调用单例类内的其他函数,一般用于实现工具类,可以达到节省内存的目的,始终只存在唯一的实例,只开一次空间
 */
class SingleInstance {
    //1.private修饰构造方法:不让类外创建对象(不能new)
    private SingleInstance() {

    }

    //2.private修饰静态变量:存放唯一对象,同时就在堆中创建唯一实例,不管以后用不用
    private static SingleInstance si = new SingleInstance();

    //3.public修饰静态方法:使得类外无须实例化也可以用类名调用,该方法用来返回类内部创建的唯一实例
    public static SingleInstance getSingleInstanceObject() {
        return si;
    }

    //类内的其他静态函数可能并不需要实例来调用,直接类名调用即可,导致饿汉模式中提前创建的唯一实例浪费了
    public static void otherFunction() {

    }
}

/**
 * 与 饿汉模式 相对的是 懒汉模式 , 懒汉模式以时间换空间,创建对象浪费时间,但是不会导致浪费内存
 */
class SingleInstance2 {
    private SingleInstance2() {
    }

    private static SingleInstance2 si2;

    public static SingleInstance2 getSingleInstance2Object() {
        if (si2 == null) {//首次调用才会开空间,获取实例,之后再次调用将不会创建实例
            si2 = new SingleInstance2();
        }
        return si2;
    }

    //即使类内其他静态函数直接通过类名调用,也不会导致浪费创建唯一实例的空间,但是代价是下次需要使用唯一实例时,需要判断然后再创建才能使用,以时间换空间
    public static void otherFunction() {

    }
    /**
     * 饿汉模式 与 懒汉模式 互相弥补后成为了登记模式...待补充
     */
}

class CodeBlock {

    static int a = 1;//变量初始化'a''1'是冗余的
    static int b;//未赋初值,使用默认值0
    static int c = 5;//静态成员变量后定义初始化为5 , 最终执行结果是 c = 5 , 如果将此行移动到static代码块之前 , 那么最终c的值是 c = 3

    //1.静态代码块
    static {
        a = 2;//如果没有这行,那么a的值还是1
//        a += 2;//如果没有上一行,那么结果是 a = 3,会使用a的旧值即static int a = 1
        b++;//b = 1,b没有赋初值,默认初值是0,b++使用旧值,运算结束后b = 1
        System.out.println("1 静态代码块执行了");
        c = 3;//静态代码块中先给c赋值3
    }


    //2.构造代码块
    {
        System.out.println("2 构造代码块执行了");
    }

    public CodeBlock() {
        //3.局部代码块
        {
            System.out.println("3 空参构造中的局部代码块被调用了");
            int num = 10;
        }
        //局部代码块之外,num不可见,提前结束变量的生存期,释放空间
    }
}


class Student {

    private String name;
    private int id;
    public static String test = "ikun";

    public static void haha() {
        System.out.println("haha");
    }

    /**
     * 展示数据
     */
    public void show() {
        System.out.println(this.name + " - " + this.id + " - " + test);
    }


    public Student(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

/**
 * 不允许实例化,只能通过类名调用静态内部成员函数的工具类
 */
class MyUtility {
    //使用私有的构造函数,只允许通过 类名. 访问,不允许实例化
    private MyUtility() {

    }

    private static String example = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";//private限制外界无法修改,static使得只会往方法区加载一次


    //生成随机验证码
    public static String getCheckCode() {
        String result = "[ ";
        Random rd = new Random();
        for (int i = 0; i < 4; i++) {
            result += example.charAt(rd.nextInt(example.length()));//example.length刚好包头不包尾
        }
        return result + " ]";
    }
}
